package org.light.generator;

import java.util.ArrayList;
import java.util.List;

import org.light.core.Writeable;
import org.light.domain.Domain;
import org.light.domain.Statement;
import org.light.domain.StatementList;
import org.light.utils.DomainUtil;
import org.light.utils.StringUtil;
import org.light.utils.WriteableUtil;

public class DbContextGenerator extends Generator{
	protected List<Domain> domains = new ArrayList<>();
	protected String projectName;
	protected String edition;
	public String getProjectName() {
		return projectName;
	}

	public void setProjectName(String projectName) {
		this.projectName = projectName;
	}

	public String getEdition() {
		return edition;
	}

	public void setEdition(String edition) {
		this.edition = edition;
	}

	public List<Domain> getDomains() {
		return domains;
	}

	public void setDomains(List<Domain> domains) {
		this.domains = domains;
	}

	public DbContextGenerator(){
		super();
		super.fileName = "db_context.rs";
		super.standardName = "DbContext";
	}

	@Override
	public StatementList generateStatementList() {
		List<Writeable> sList = new ArrayList<Writeable>();
		long serial = 1000L;
		sList.add(new Statement(serial+1000L,0,"use super::"+DomainUtil.domainListToCapFirstDomainNameWithSuffixListStr(this.domains)+";"));
		sList.add(new Statement(serial+2000L,0,"use sqlx::mysql::MySqlRow;"));
		sList.add(new Statement(serial+3000L,0,"use sqlx::{FromRow, MySqlPool};"));
		sList.add(new Statement(serial+4000L,0,"use std::sync::Arc;"));
		sList.add(new Statement(serial+5000L,0,""));
		sList.add(new Statement(serial+6000L,0,"pub struct Table<'c, T>"));
		sList.add(new Statement(serial+7000L,0,"where"));
		sList.add(new Statement(serial+8000L,1,"T: FromRow<'c, MySqlRow<'c>>,"));
		sList.add(new Statement(serial+9000L,0,"{"));
		sList.add(new Statement(serial+10000L,1,"pub pool: Arc<MySqlPool>,"));
		sList.add(new Statement(serial+11000L,1,"_from_row: fn(&MySqlRow<'c>) -> Result<T, sqlx::Error>,"));
		sList.add(new Statement(serial+12000L,0,"}"));
		sList.add(new Statement(serial+13000L,0,""));
		sList.add(new Statement(serial+14000L,0,"impl<'c, T> Table<'c, T>"));
		sList.add(new Statement(serial+15000L,0,"where"));
		sList.add(new Statement(serial+16000L,1,"T: FromRow<'c, MySqlRow<'c>>,"));
		sList.add(new Statement(serial+17000L,0,"{"));
		sList.add(new Statement(serial+18000L,1,"fn new(pool: Arc<MySqlPool>) -> Self {"));
		sList.add(new Statement(serial+19000L,2,"Table {"));
		sList.add(new Statement(serial+20000L,3,"pool,"));
		sList.add(new Statement(serial+21000L,3,"_from_row: T::from_row,"));
		sList.add(new Statement(serial+22000L,2,"}"));
		sList.add(new Statement(serial+23000L,1,"}"));
		sList.add(new Statement(serial+24000L,0,"}"));
		sList.add(new Statement(serial+25000L,0,""));
		sList.add(new Statement(serial+26000L,0,"pub struct JoinTable<'c, T1, T2>"));
		sList.add(new Statement(serial+27000L,0,"where"));
		sList.add(new Statement(serial+28000L,1,"T1: FromRow<'c, MySqlRow<'c>>,"));
		sList.add(new Statement(serial+29000L,1,"T2: FromRow<'c, MySqlRow<'c>>,"));
		sList.add(new Statement(serial+30000L,0,"{"));
		sList.add(new Statement(serial+31000L,1,"pub pool: Arc<MySqlPool>,"));
		sList.add(new Statement(serial+32000L,1,"_from_row: ("));
		sList.add(new Statement(serial+33000L,2,"fn(&MySqlRow<'c>) -> Result<T1, sqlx::Error>,"));
		sList.add(new Statement(serial+34000L,2,"fn(&MySqlRow<'c>) -> Result<T2, sqlx::Error>,"));
		sList.add(new Statement(serial+35000L,1,"),"));
		sList.add(new Statement(serial+36000L,0,"}"));
		sList.add(new Statement(serial+37000L,0,""));
		sList.add(new Statement(serial+38000L,0,"impl<'c, T1, T2> JoinTable<'c, T1, T2>"));
		sList.add(new Statement(serial+39000L,0,"where"));
		sList.add(new Statement(serial+40000L,1,"T1: FromRow<'c, MySqlRow<'c>>,"));
		sList.add(new Statement(serial+41000L,1,"T2: FromRow<'c, MySqlRow<'c>>,"));
		sList.add(new Statement(serial+42000L,0,"{"));
		sList.add(new Statement(serial+43000L,1,"fn new(pool: Arc<MySqlPool>) -> Self {"));
		sList.add(new Statement(serial+44000L,2,"JoinTable {"));
		sList.add(new Statement(serial+45000L,3,"pool,"));
		sList.add(new Statement(serial+46000L,3,"_from_row: (T1::from_row, T2::from_row),"));
		sList.add(new Statement(serial+47000L,2,"}"));
		sList.add(new Statement(serial+48000L,1,"}"));
		sList.add(new Statement(serial+49000L,0,"}"));
		sList.add(new Statement(serial+50000L,0,""));
		sList.add(new Statement(serial+51000L,0,"pub struct Database<'c> {"));
		serial += 52000L;
		for (Domain d: this.domains) {
			sList.add(new Statement(serial,1,"pub "+StringUtil.getSnakeName(d.getPlural())+": Arc<Table<'c, "+d.getCapFirstDomainNameWithSuffix()+">>,"));
			serial += 1000L;
		}
		sList.add(new Statement(serial+68000L,0,"}"));
		sList.add(new Statement(serial+69000L,0,""));
		sList.add(new Statement(serial+70000L,0,"impl Database<'_> {"));
		sList.add(new Statement(serial+71000L,1,"pub async fn new(sql_url: &str) -> Database<'_> {"));
		sList.add(new Statement(serial+72000L,2,"let pool = MySqlPool::new(sql_url).await.unwrap();"));
		sList.add(new Statement(serial+73000L,2,"let pool = Arc::new(pool);"));
		sList.add(new Statement(serial+74000L,0,""));
		sList.add(new Statement(serial+75000L,2,"Database {"));
		for (Domain d: this.domains) {
			sList.add(new Statement(serial+76000L,3,StringUtil.getSnakeName(d.getPlural())+": Arc::from(Table::new(pool.clone())),"));
			serial += 1000L;
		}
		sList.add(new Statement(serial+92000L,2,"}"));
		sList.add(new Statement(serial+93000L,1,"}"));
		sList.add(new Statement(serial+94000L,0,"}"));

		return WriteableUtil.merge(sList);
	}

}
